﻿using System;
using System.Collections.Generic;
using System.Text;

using Obsidian.Utils;
using Obsidian.Edm;
using Obsidian.Data.Sql;
using Obsidian.Data.Sql.OleDbClient;
using Obsidian.Data.Sql.SqlSrvClient;
using Oak.Core.Model;

namespace Oak.Core.Data
{
    public class TreeDA<T> where T : ITreeNodeInfo, new()
    {

        protected DataReader SelectList(IDbHandler dbh, T node, ITreeNodeQueryInfo qry)
        {

            if (!qry.NodeIds.IsNull)
                dbh.Where(node.NodeId.In(qry.NodeIds.Value));

            if (!qry.Enabled.IsNull)
                dbh.Where(node.Enabled.Equals(qry.Enabled.Value));

            if (qry.Paths.Count > 0)
                dbh.Where(node.Path.Like(qry.Paths.Value, LogicalOperator.OR, LikeType.Left));
     
            dbh.OrderBy(node.Depth);
            dbh.OrderBy(node.SortOrder);

            return dbh.Select();

        }

        protected T InsertNode(T node)
        {
            if (!Amend(node))
                return default(T);
            node.Save();
            return node;
        }

        protected T UpdateNode(T node)
        {
            if (!Amend(node))
                return default(T);
            node.Update();
            return node;
        }

        private static bool Amend(T node)
        {
            if (node.ParentId.IsNull)
                node.ParentId.Set(0);
            var parentId = node.ParentId.Value;
            
            if (parentId < 0)
                throw new Exception("父节点ID不能小于0");
            
            if (parentId == 0)
            {
                node.Depth.Set(0);
                node.Path.Set(",0,");
            }
            else if (parentId > 0)
            {
                T parent = new T();
                if (!parent.GetByPrimaryKey(parentId))
                    throw new Exception("父节点不存在");

                node.Depth.Set(parent.Depth.Value + 1);
                node.Path.Set(parent.Path.Value + parent.NodeId.Value + ",");
            }
            int maxSortOrder = SelectMaxSortOrderByParentId(parentId);
            node.SortOrder.Set(maxSortOrder);
            return true;
        }


        private static int SelectMaxSortOrderByParentId(long parentId)
        {
            T t = new T();
            string sqlText = " SELECT MAX({0}) FROM {1} WHERE {2} = {3} ";
            sqlText = String.Format(sqlText, 
                t.SortOrder.GetDbField().Name,
                t.DbTableName,
                t.ParentId.GetDbField().Name,
                parentId);
            object val;
            if (t.GetDataBase().Type == DataBaseType.OleDb)
            {
                OleDbExecutor exec = new OleDbExecutor(
                    t.GetDataBase().ConnectionString);
                val = exec.ExecuteScalar(sqlText);
            }
            else
            {
                SqlSrvExecutor exec = new SqlSrvExecutor(
                    t.GetDataBase().ConnectionString);
                val = exec.ExecuteScalar(sqlText);
            }
            return Convert.IsDBNull(val) ? -1 : Convert.ToInt32(val);
        }

        public static List<T> SelectNodesFullPath(IEnumerable<long> nodeIds)
        {
            T t = new T();
            string sqlText = SqlTextLoader.Get("Oak.Core.Data.TreeDA.SelectNodesFullPath");
            sqlText = sqlText.Replace("$T_TREE$", t.DbTableName)
                .Replace("$NODE_IDS$", ArrayUtil.Join(nodeIds, ","))
                .Replace("$F_NODE_ID$", t.NodeId.DbFieldName)
                .Replace("$F_DEPTH$", t.Depth.DbFieldName);

            SqlSrvExecutor exr = new SqlSrvExecutor(t.GetDataBase().ConnectionString);
            DataReader dr = new DataReader(exr.ExecuteReader(sqlText));
            List<T> list = new List<T>();
            while (dr.Read())
            {
                T o = new T();
                foreach (IModelField field in o.Fields)
                {
                    DbField dbField = field.GetDbField();
                    if (dbField == null)
                        continue;
                    string dbFieldName = dbField.Name;
                    field.Set(dr.GetValue(dbFieldName));
                }
                list.Add(o);
            }
            dr.Close();
            return list;
        }

    }
}
